using Microsoft.VisualStudio.TestTools.UnitTesting;

using System.Collections.Generic;

using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;
using Weazel.Gezel.Parser;
using Weazel.Math;

namespace weazel.gezel.parser.test
{
  [TestClass]
  public class TestParseDatapath
  {
    [TestMethod]
    public void TestParseSimpleDatapath1()
    {
      Tokenizer t = new Tokenizer("dp mydp {}");

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual("mydp", dp.LocalName);
      Assert.AreEqual(m, dp.Parent);
    }

    [TestMethod]
    public void TestParseSimpleDatapathPorts()
    {
      Tokenizer t = new Tokenizer("dp mydp (in in_a, in_b: tc(32)){}");

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual("mydp", dp.LocalName);
      Assert.AreEqual(m, dp.Parent);

      Assert.AreEqual(dp, dp.InPorts["in_a"].Parent);
      Assert.AreEqual(32, dp.InPorts["in_a"].Width);
      Assert.AreEqual(true, dp.InPorts["in_a"].Signed);
      Assert.AreEqual("mydp.in_a", dp.InPorts["in_a"].Name);
      Assert.AreEqual(0, dp.InPorts["in_a"].DeclarationPosition);

      Assert.AreEqual(dp, dp.InPorts["in_b"].Parent);
      Assert.AreEqual(32, dp.InPorts["in_b"].Width);
      Assert.AreEqual(true, dp.InPorts["in_b"].Signed);
      Assert.AreEqual("mydp.in_b", dp.InPorts["in_b"].Name);
      Assert.AreEqual(1, dp.InPorts["in_b"].DeclarationPosition);
    }

    [TestMethod]
    public void TestParseDatapathPorts()
    {
      List<ParseError> errors = new List<ParseError>();
      string description =
              "dp mydp (out out_z : ns(32); in load : ns(1); in in_z, in_y : tc(256)){}";

      Tokenizer t = new Tokenizer(description);

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(dp.Name, "mydp");
      Assert.AreEqual(dp.Parent, m);

      Dictionary<string, InPort> ports = dp.InPorts;
      Assert.AreEqual(ports["load"].Parent, dp);
      Assert.AreEqual(ports["load"].Width, 1);
      Assert.AreEqual(ports["load"].Signed, false);
      Assert.AreEqual(ports["load"].Name, "mydp.load");
      Assert.AreEqual(ports["load"].DeclarationPosition, 1);

      Assert.AreEqual(ports["in_z"].Parent, dp);
      Assert.AreEqual(ports["in_z"].Width, 256);
      Assert.AreEqual(ports["in_z"].Signed, true);
      Assert.AreEqual(ports["in_z"].Name, "mydp.in_z");
      Assert.AreEqual(ports["in_z"].DeclarationPosition, 2);

      Assert.AreEqual(ports["in_y"].Parent, dp);
      Assert.AreEqual(ports["in_y"].Width, 256);
      Assert.AreEqual(ports["in_y"].Signed, true);
      Assert.AreEqual(ports["in_y"].Name, "mydp.in_y");
      Assert.AreEqual(ports["in_y"].DeclarationPosition, 3);


      Dictionary<string, OutPort> outPorts = dp.OutPorts;
      Assert.AreEqual(outPorts["out_z"].Parent, dp);
      Assert.AreEqual(outPorts["out_z"].Width, 32);
      Assert.AreEqual(outPorts["out_z"].Signed, false);
      Assert.AreEqual(outPorts["out_z"].Name, "mydp.out_z");
      Assert.AreEqual(outPorts["out_z"].DeclarationPosition, 0);
    }

    [TestMethod]
    public void TestParseRegister()
    {
      List<ParseError> errors = new List<ParseError>();
      string description =
              "dp mydp { reg rx : ns(1); }";
      Tokenizer t = new Tokenizer(description);

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(dp.Name, "mydp");
      Assert.AreEqual(dp.Parent, m);

      Dictionary<string, Register> registers = dp.Registers;
      Assert.AreEqual(registers.Count, 1);
      Assert.AreEqual(registers["rx"].Name, "mydp.rx");
      Assert.AreEqual(registers["rx"].Width, 1);
      Assert.AreEqual(registers["rx"].Parent, dp);
      Assert.AreEqual(registers["rx"].Signed, false);
    }

    [TestMethod]
    public void TestParseRegisters()
    {
      List<ParseError> errors = new List<ParseError>();
      string description =
              "dp mydp { reg rx, ry, rz : tc(32); }";
      Tokenizer t = new Tokenizer(description);

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(dp.Name, "mydp");
      Assert.AreEqual(dp.Parent, m);

      Dictionary<string, Register> registers = dp.Registers;
      Assert.AreEqual(registers.Count, 3);
      Assert.AreEqual(registers["rx"].Name, "mydp.rx");
      Assert.AreEqual(registers["rx"].Width, 32);
      Assert.AreEqual(registers["rx"].Parent, dp);
      Assert.AreEqual(registers["rx"].Signed, true);

      Assert.AreEqual(registers["ry"].Name, "mydp.ry");
      Assert.AreEqual(registers["ry"].Width, 32);
      Assert.AreEqual(registers["ry"].Parent, dp);
      Assert.AreEqual(registers["ry"].Signed, true);

      Assert.AreEqual(registers["rz"].Name, "mydp.rz");
      Assert.AreEqual(registers["rz"].Width, 32);
      Assert.AreEqual(registers["rz"].Parent, dp);
      Assert.AreEqual(registers["rz"].Signed, true);
    }

    [TestMethod]
    public void TestParseSignal()
    {
      List<ParseError> errors = new List<ParseError>();
      string description =
              "dp mydp { sig sx : ns(1); }";
      Tokenizer t = new Tokenizer(description);

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(dp.Name, "mydp");
      Assert.AreEqual(dp.Parent, m);

      Dictionary<string, Signal> signals = dp.Signals;
      Assert.AreEqual(signals.Count, 1);
      Assert.AreEqual(signals["sx"].Name, "mydp.sx");
      Assert.AreEqual(signals["sx"].Width, 1);
      Assert.AreEqual(signals["sx"].Parent, dp);
      Assert.AreEqual(signals["sx"].Signed, false);
    }

    [TestMethod]
    public void TestParseSignals()
    {
      List<ParseError> errors = new List<ParseError>();
      string description =
              "dp mydp { sig sx, sy, sz : tc(32); }";
      Tokenizer t = new Tokenizer(description);

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(dp.Name, "mydp");
      Assert.AreEqual(dp.Parent, m);

      Dictionary<string, Signal> signals = dp.Signals;
      Assert.AreEqual(signals.Count, 3);

      Assert.AreEqual(signals["sx"].Name, "mydp.sx");
      Assert.AreEqual(signals["sx"].Width, 32);
      Assert.AreEqual(signals["sx"].Parent, dp);
      Assert.AreEqual(signals["sx"].Signed, true);

      Assert.AreEqual(signals["sy"].Name, "mydp.sy");
      Assert.AreEqual(signals["sy"].Width, 32);
      Assert.AreEqual(signals["sy"].Parent, dp);
      Assert.AreEqual(signals["sy"].Signed, true);

      Assert.AreEqual(signals["sz"].Name, "mydp.sz");
      Assert.AreEqual(signals["sz"].Width, 32);
      Assert.AreEqual(signals["sz"].Parent, dp);
      Assert.AreEqual(signals["sz"].Signed, true);
    }

    [TestMethod]
    public void TestParseLookup()
    {
      List<ParseError> errors = new List<ParseError>();
      Tokenizer t = new Tokenizer("dp mydp { lookup T : ns(8) = {1, 2, 3}; }");

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];

      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(dp.Name, "mydp");
      Assert.AreEqual(dp.Parent, m);

      Dictionary<string, LookupTable> luts = dp.LookupTables;
      Assert.AreEqual(luts.Count, 1);
      LookupTable lut = luts["T"];
      Integer[] values = lut.Values;

      Assert.AreEqual(lut.Name, "mydp.T");
      Assert.AreEqual(lut.Width, 8);
      Assert.AreEqual(lut.Signed, false);

      Assert.AreEqual(values.Length, 3);
      Assert.AreEqual(values[0].ToInt(), 1);
      Assert.AreEqual(values[1].ToInt(), 2);
      Assert.AreEqual(values[2].ToInt(), 3);
    }

    [TestMethod]
    public void TestParseDatapathWithTracer()
    {
      List<ParseError> errors = new List<ParseError>();
      const string simpleDesc = "dp mydp { $trace(a, \"a.txt\"); }";
      Tokenizer t = new Tokenizer(simpleDesc);

      Model m = new Model("model");

      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(0, t.Errors.Count);

      Datapath dp = m.Datapaths["mydp"];    
     
      Assert.AreEqual(errors.Count, 0);
      Assert.AreEqual(1, dp.Tracers.Count);
      Assert.AreEqual("a", (dp.Tracers[0].Expression as LeafExpression).Value);
      Assert.AreEqual("a.txt", dp.Tracers[0].Filename);
    }

    [TestMethod]
    public void TestParseDatapathWithMultiplyDefinedSfgs()
    {
      List<ParseError> errors = new List<ParseError>();
      Tokenizer t = new Tokenizer("dp mydp { sfg a {} sfg a{} }");
      Model model = new Model("my_model");
      ParseDatapath.Parse(new Set<Token>(Token.Eof), t, model);

      Assert.AreEqual(1, t.Errors.Count);
      Assert.AreEqual(ParseErrorType.DuplicateSfgDefinition, t.Errors[0].ErrorType);
    }

    [TestMethod]
    public void TestParseDatapathWithMultiplyDefinedDatapaths()
    {
      List<ParseError> errors = new List<ParseError>();

      ParseModel.Parse(ref errors, "dp mydp {} dp mydp {}");

      Assert.AreEqual(1, errors.Count);
      Assert.AreEqual(ParseErrorType.DuplicateDatapathDefintion, errors[0].ErrorType);
    }
  }
}





